package blackstar.plugin.ui;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import blackstar.agent.AgentConfig;
import blackstar.plugin.ui.vo.DetailInvokeStack;
import blackstar.plugin.ui.vo.PerformanceAgentInfo;
import blackstar.plugin.ui.vo.SimpleStatItem;

/**
 * @author RayWoo (ayufox@gmail.com)
 * 
 */
public class PerformanceServlet extends HttpServlet
{
	private final static Log LOGGER = LogFactory.getLog(MonitorServlet.class);

	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException
	{
		String uri = request.getRequestURI();
		if (!"/".equals(request.getContextPath()))
		{
			uri = uri.substring(request.getContextPath().length());
		}
		LOGGER.info("request:" + uri);
		String[] pathArray = uri.substring(1).split("/");
		if (pathArray.length == 1)
		{
			LOGGER.info("Error Request Path:" + uri);
			response.sendError(404);
		} else if (pathArray.length == 2)
		{
			String name = pathArray[1];
			printPerformanceList(request, response, uri, name);
		} else if (pathArray.length == 3)
		{
			String jvmid = pathArray[1];
			String file = pathArray[2];
			if ("agent".equals(file))
			{
				printPerformanceAgent(request, response, jvmid);
			}
			else if ("config".equals(file))
			{
				printPerformanceConfig(request, response, jvmid);
			}
			else if ("test".equals(file))
			{
				printTest(request, response, jvmid);
			}
			else if ("reload".equals(file))
			{
				printReload(request, response, jvmid);
			}
			else if (file.startsWith("simple"))
			{
				printPerformanceSimple(request, response, jvmid, file);
			} else if (file.startsWith("detail"))
			{
				printPerformanceDetail(request, response, jvmid, file);
			} else
			{
				LOGGER.info("Error Request Path:" + uri);
				response.sendError(404);
			}
		} else
		{
			LOGGER.info("Error Request Path:" + uri);
			response.sendError(404);
		}

	}

	private void printReload(HttpServletRequest request,
			HttpServletResponse response, String jvmid) throws IOException
	{
		String message = JMXProxyUtils.reloadConfig(jvmid);
		PrintWriter writer = response.getWriter();
		writer.print("{");
		writer.print("message:'");
		writer.print(message);
		writer.print("'}");
	}

	private void printTest(HttpServletRequest request,
			HttpServletResponse response, String jvmid) throws IOException
	{
		String className = request.getParameter("className");
		String methodName = request.getParameter("methodName");
		LOGGER.info("className[" + className + "] methodName[" + methodName + "]");
		PrintWriter writer = response.getWriter();
		writer.print("{");
		writer.print("result:0");
		writer.print("}");
	}

	private void printPerformanceConfig(HttpServletRequest request,
			HttpServletResponse response, String jvmid) throws IOException
	{
		File file = new File(AgentConfig.getConfigPath() + File.separator + "performance" + File.separator  + jvmid + ".xml");
		if (file.exists())
		{
			response.setContentType("application/xml; charset=utf-8");
			InputStream is = null;
			try
			{
				is = new FileInputStream(file);
				byte[] buffer = new byte[256 * 1024];
				int length = 0;
				OutputStream os = response.getOutputStream();
				while (true)
				{
					length = is.read(buffer);
					if (length > 0)
					{
						os.write(buffer, 0, length);
					}
					if (length < buffer.length)
					{
						break;
					}
				}
				os.flush();
			}
			catch (FileNotFoundException e)
			{
				PrintWriter writer = response.getWriter();
				writer.print("配置不存在");
				writer.flush();
			}
			finally
			{
				is.close();
			}
		}
		else
		{
			PrintWriter writer = response.getWriter();
			writer.print("配置不存在");
			writer.flush();
		}
	}

	private void printPerformanceAgent(HttpServletRequest request,
			HttpServletResponse response, String jvmid) throws IOException
	{
		PerformanceAgentInfo agentInfo = JMXProxyUtils.getPerformanceAgentInfo(jvmid);
		if (agentInfo == null)
		{
			LOGGER.info("jvmid[" + jvmid + "] not found or no performance agent");
			response.sendError(404);
		}
		else
		{
			PrintWriter writer = response.getWriter();
			writer.print("{");
			writer.print("type:'");
			writer.print(agentInfo.getType());
			writer.print("', bufferSize : '");
			writer.print(agentInfo.getBufferSize());
			writer.print("', logTimes :'");
			writer.print(agentInfo.getLogTimes());
			writer.print("', logAvarageTime :'");
			writer.print(agentInfo.getLogAvarageTime());
			writer.print("', logBytes:'");
			writer.print(agentInfo.getLogBytes());
			writer.print("', invokeMessage:'");
			writer.print(agentInfo.getInvokeMessage());
			writer.print("'}");
		}
	}

	private void printPerformanceSimple(HttpServletRequest request,
			HttpServletResponse response, String jvmid, String fileName)
			throws IOException
	{
		File file = new File(AgentConfig.getPerformanceDataPath(jvmid),
				fileName);
		if (file.exists())
		{
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String line = reader.readLine();

			String[] heads = new String[0];
			String[] headArray = null;
			List<SimpleStatItem> result = new ArrayList<SimpleStatItem>();
			double allTime = 0;
			if (line != null)
			{
				headArray = line.split("\\|");
				if (headArray.length >= 4)
				{
					heads = new String[headArray.length - 4];
					for (int i = 4; i < headArray.length; i++)
					{
						heads[i - 4] = headArray[i] + "(ms)";
					}

					int ignore = 0;
					while ((line = reader.readLine()) != null)
					{
						String[] array = line.split("\\|");
						if (array.length < heads.length + 4)
						{
							ignore++;
						} else
						{
							try
							{
								String className = array[0];
								String methodName = array[1];
								long totalInvokes = Long.parseLong(array[2]);
								double totalTime = Double.parseDouble(array[3])
										/ (1000 * 1000); // tran
								// to ms
								long[] threadholdCounts = new long[heads.length];
								for (int i = 0; i < heads.length; i++)
								{
									threadholdCounts[i] = Long
											.parseLong(array[i + 4]);
								}

								SimpleStatItem item = new SimpleStatItem();
								item.setClassName(className);
								item.setMethodName(methodName);
								item.setTotalInvokes(totalInvokes);
								item.setTotalTime(totalTime);
								item.setThreadholdCounts(threadholdCounts);
								result.add(item);
								allTime += totalTime;
							} catch (NumberFormatException e)
							{
								ignore++;
							}
						}

						array = null;
					}

					Collections.sort(result, new Comparator<SimpleStatItem>()
					{
						public int compare(SimpleStatItem o1, SimpleStatItem o2)
						{
							return (new Double(o2.getTotalTime()).compareTo(o1
									.getTotalTime()));
						}
					});

				}
			}

			PrintWriter writer = response.getWriter();
			writer.print("{");
			writer.print("total : ");
			writer.print(result.size());
			writer.print(",list : [");
			int index = 0;
			NumberFormat format = NumberFormat.getInstance();
			format.setMaximumIntegerDigits(3);
			format.setGroupingUsed(false);
			for (SimpleStatItem item : result)
			{
				if (index > 0)
				{
					writer.print(",");
				}
				writer.print("{");
				writer.print("name : '");
				writer.print(item.getClassName() + "." + item.getMethodName());
				writer.print("', totalInvokes :" );
				writer.print(item.getTotalInvokes());
				writer.print(", avarage :" );
				writer.print(format.format(item.getTotalTime() / item.getTotalInvokes()));
				writer.print(", percent :" );
				writer.print(format.format(item.getTotalTime() / allTime));
				writer.print(", totalTime :" );
				writer.print(item.getTotalTime());
				writer.print(", lt10ms :" );
				writer.print(item.getThreadholdCounts()[0]);
				writer.print(", lt50ms :" );
				writer.print(item.getThreadholdCounts()[1]);
				writer.print(", lt100ms :" );
				writer.print(item.getThreadholdCounts()[2]);
				writer.print(", lt300ms :" );
				writer.print(item.getThreadholdCounts()[3]);
				writer.print(", lt500ms :" );
				writer.print(item.getThreadholdCounts()[4]);
				writer.print(", lt1s :" );
				writer.print(item.getThreadholdCounts()[5]);
				writer.print(", lt3s :" );
				writer.print(item.getThreadholdCounts()[6]);
				writer.print(", ge3s :" );
				writer.print(item.getThreadholdCounts()[07]);
				writer.print("}");
				
				index++;
			}
			writer.print("]}");
			response.getWriter().flush();			

		} else
		{
			LOGGER.info("Error Request Path: [jvmid=" + jvmid + ",fileName="
					+ fileName + "]");
			response.sendError(404);
		}
	}

	private void printPerformanceDetail(HttpServletRequest request,
			HttpServletResponse response, String jvmid, String fileName)
			throws IOException
	{
		File file = new File(AgentConfig.getPerformanceDataPath(jvmid),
				fileName);
		if (file.exists())
		{
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String line = reader.readLine();

			String[] headArray = null;
			List<DetailInvokeStack> result = new ArrayList<DetailInvokeStack>();
			if (line != null)
			{
				headArray = line.split("\\|");
				if (headArray.length >= 4)
				{
					int ignore = 0;

					while ((line = reader.readLine()) != null)
					{
						String[] array = line.split("\\|");
						if (array.length < 6)
						{
							ignore++;
						} else
						{
							try
							{
								int level = Integer.parseInt(array[0]);
								String className = array[1];
								String methodName = array[2];
								long totalInvokes = Long.parseLong(array[3]);
								if (totalInvokes > 0)
								{
									double totalTime = Double
											.parseDouble(array[4])
											/ (1000 * 1000); // tran
									String percent = array[5];
									DetailInvokeStack invoke = new DetailInvokeStack(
											className, methodName,
											totalInvokes, totalTime, percent,
											level);
									if (level == 0)
									{
										result.add(invoke);
									} else if (result.size() > 0)
									{

										result.get(result.size() - 1).addChild(
												invoke);
									}
								} else
								{
									ignore++;
								}
							} catch (NumberFormatException e)
							{
								ignore++;
							}
						}

						array = null;
					}
				}
			}

			StringBuilder json = new StringBuilder();
			buildList(result, json);

			response.getWriter().print(json.toString());
			response.getWriter().flush();
		} else
		{
			LOGGER.info("Error Request Path: [jvmid=" + jvmid + ",fileName="
					+ fileName + "]");
			response.sendError(404);
		}

	}

	private void buildList(List<DetailInvokeStack> list, StringBuilder json)
	{
		int idx = 0;
		json.append("[");
		for (DetailInvokeStack invoke : list)
		{
			if (idx > 0)
			{
				json.append(",");
			}
			buildItem(invoke, json);
			idx++;
		}
		json.append("]");
	}

	private void buildItem(DetailInvokeStack invoke, StringBuilder json)
	{
		json.append("{");
		String name = invoke.getClassName();
		int index = name.lastIndexOf('.');
		if (index > 0)
		{
			name = name.substring(index + 1);
		}
		name = name + "." + invoke.getMethodName();
		json.append("name:'" + name + "'");
		json.append(",totalInvokes:" + invoke.getTotalInvokes());
		NumberFormat format = NumberFormat.getInstance();
		format.setMaximumFractionDigits(3);
		format.setGroupingUsed(false);
		json.append(",avarage:"
				+ format.format(invoke.getTotalTime()
						/ invoke.getTotalInvokes()));
		json.append(",percent:" + invoke.getPercent());
		if (invoke.getChildren().size() > 0)
		{
			json.append(",children:");
			buildList(invoke.getChildren(), json);
		} else
		{
			json.append(",leaf:true");
		}
		json.append("}");
	}

	private void printPerformanceList(HttpServletRequest request,
			HttpServletResponse response, String uri, String jvmid)
			throws IOException
	{
		File dir = new File(AgentConfig.getPerformanceDataPath(jvmid));

		LOGGER.info("list [" + dir.getAbsolutePath() + "]");

		List<String> files = new ArrayList<String>();
		if (dir.exists())
		{
			File[] list = dir.listFiles();
			for (File f : list)
			{
				files.add(f.getName());
			}
			Collections.sort(files, new Comparator<String>()
			{
				public int compare(String o1, String o2)
				{
					return o2.compareTo(o1);
				}
			});
		}

		PrintWriter writer = response.getWriter();
		writer.print("[");
		int index = 0;
		for (String file : files)
		{
			if (index > 0)
			{
				writer.print(",");
			}
			writer.print("{");
			writer.print("value : '");
			writer.print(file);
			writer.print("', text :'");
			writer.print(file);
			writer.print("'}");
			index++;
		}
		writer.print("]");
		writer.flush();
	}
}
